/***************************************************************************************************
*Copyright(C),2010-2016,Sumscope
*FileName	:  BondKeyHashMap.cpp
*Author	:  scofined.qi
*Version	:  1.0
*Date		:  2020/05/12
*Desc		:  //用于主要说明此程序文件完成的主要功能
*Relation :
*Others	:  //其他内容说明
*Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
*History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
***************************************************************************************************/
#include "BondKeyHashMap.h"
#include <core/strings/string_util.h>

namespace{
	enum{
		kTypeLen = 3,		// 类型编码的长度
		kSubLen = 3,		// 子类型的编码长度
		kYearLen = 4,		// 年份长度
		kDictMax = 255,		// 小字典中能容纳的最大个数
		kInvalidHead = 0,		// 0表示bkey的head空值
		kSeqZeroMax = 2,		// 16bit中,最高位有多少个用于表示0字符
		kMaxBondId = 1 << 22,	// 400w债券,这个值和kYearBase根bkey中的year占的位个数有关
		kYearBase = 2000,		// 年份最大支持范围2000-2032(1950+2^10))
		kYearMax = 2032		// 这个值和kYearBase根bkey中的year占的位个数有关
	};
}
NameDictHash::NameDictHash(int capcity /*= 6*/, const char* str /*= nullptr*/) :m_count(0), m_capcity(1 << capcity), m_bitcount(capcity)
{
	m_array = new NodeInfo*[m_capcity];
	memset(m_array, 0, m_capcity*sizeof(NodeInfo*));
}

NameDictHash::~NameDictHash()
{
	for (int i = 0; i < m_capcity; i++)
	{
		NodeInfo* ptr = m_array[i];
		while (ptr)
		{
			NodeInfo* temp = ptr;
			ptr = ptr->next;
			delete temp;
		}
	}
}

void NameDictHash::Init(const char* str)
{
	if (str)
	{
		std::vector<qb::base::strinfo> tokens; tokens.reserve(m_capcity);
		qb::base::StringUtil::SplitStringInfo(str, strlen(str), ",", tokens);
		for (int i = 0; i < (int)tokens.size(); i++)
		{
			if (tokens[i].len == 3)
			{
				Add(str + tokens[i].pos);
			}
		}
	}
}

int NameDictHash::Add(const char* str)
{
	unsigned int hash = 5381;
	hash += (hash << 5) + (str[0]);
	hash += (hash << 5) + (str[1]);
	hash += (hash << 5) + (str[2]);
	hash = hash & 0x7FFFFFFF;

	// 避免除法,提升重大非常明显
	int pos = (hash >> m_bitcount)&(m_capcity - 1);	//hash%m_capcity;
	NodeInfo* head = m_array[pos];
	NodeInfo* ptr = head;
	CodeId ci(str);
	while (ptr)
	{
		if (ptr->code.id == ci.id)
			return ptr->index;
		ptr = ptr->next;
	}
	int index = m_count++;
	m_array[pos] = new NodeInfo(head, index, ci);
	return index;
}

int NameDictHash::Add(int id)
{
	unsigned int hash = 0;
	int pos = (hash >> m_bitcount)&(m_capcity - 1);	//hash%m_capcity;
	int index = m_count++;
	m_array[pos] = new NodeInfo(m_array[pos], index, CodeId(id));
	return index;
}

BondKeyParser::BondKeyParser():
m_types(5), m_subs(6), m_markets(5)
{
	const char* types = "ABS,CBB,COE,COR,CRM,FIN,GBB,GOV,INT,LGB,NCD,PDB";
	const char* subs = "AMP,BGB,CBB,CBS,CCB,CCP,CEB,CMN,COB,CRM,CSB,CSP,CVB,CXB,HJB,INT,LBS,LCP,LEB,LLB,LMN,LSP,MBS,NCB,OTB,PBB,PDB,PPN,PSB,RAB,SCB,SCV,SEB,SES,SSB,TET,TLB";
	const char* marks = ".CIB,.CNN,.NNC,.SSE,.SZE,.abb";
	m_types.Init(types);
	m_subs.Init(subs);
	m_markets.Init(marks);
	// 经过测试,对于20w的数据1w个插槽是合适的,效率和内存综合最佳
	//NameDict插入时间性能有待优化,改成哈希表可能更好
	//CacheLine是整块的内存,可以预先提前分配
	//bkey可以使用编码进一步减少尺寸
	//参考F14Map,使用并行搜索,增大CacheLine尺寸会导致空间浪费
	//F14Map并行搜索:为每个节点生成一个1个字节的哈希值,将16个节点的哈希值凑成一个128位值,然后使用intel sse指令
	//一次性搜索16个
	m_subs.Add(0);
}

bool BondKeyParser::Parse(const char* str, int len, ComBondKey& key)
{
	/*
	bondkey编码规则
	A0000352020CORPPN0101.SSE
	J0001042016CORLEB0101.CIB

	^([A-Z])(\d+)(20\d{2})([A-Z]+)(\d+)(\.\w+)$

	注意:编号末尾,还有少量债券会附加备注字符,备注可能有多个,备注后面可以有数字
	其中可选的备注有:
	SH(上交所)
	SZ(深交所)
	DSC(贴现)
	US(美元)
	SQS(上清所)

	A	类别		首个字符,后续是数字
	000035	ID		//纯数字,后面是年份
	2020	年份	//年份,以20开头,4个数字,之后是字母
	CORPPN	类型	//字母字符
	0101	编号	//数字
	.CIB	市场	以点开头

	首个字符是字母
	从1位置找到第一个字母字符及其范围
	看前面4个字符是数字,且符合年份19--或者20--
	后面若干个字符是数字,再后面是.开头的市场
	*/
	int end = -1;
	if (!IsAlpha(str[0]))
		return false;
	int start = GetTypeRange(str, len, end);
	if (start < 0 || end < 0)
		return false;
	if (!(start + kTypeLen == end || start + kTypeLen + kSubLen == end))
		return false;
	//int year = ToInt<kYearLen>(str+start - kYearLen);
	int year = ToInt(str, start - kYearLen, kYearLen);
	if (year < kYearBase || year >= kYearMax)
		return false;

	int id = ToInt(str, 1, start - kYearLen - 1);
	if (start - kYearLen - 1 != 6 || id >= 65536)//发行人编号限制为6个数字
		return false;
	int seqlen = GetIntLength(str + end, len - end);
	if (seqlen <= 0)
		return false;

	// 16bit的seq拆分成两部分,高kSeqZeroMax表示0字符的个数,低位表示后面的整数值
	int seqend = end + seqlen;
	int nzero = 0;
	for (int i = end; i < seqend; i++){
		if (str[i] == '0')nzero++;
		else break;
	}
	int seq = ToInt(str, end + nzero, seqlen);
	const int nzeromax = 1 << kSeqZeroMax;
	const int seqmax = 1 << (16 - kSeqZeroMax);
	if (nzero >= nzeromax || seq >= seqmax)
		return false;

	if (str[seqend] != '.' || len - seqend != 4)
		return false;

	int type = m_types.Add(str + start);
	if (type < 0)
		return false;

	int sub = 0;
	if (start + kTypeLen + kSubLen == end)
	{
		sub = m_subs.Add(str + start + kTypeLen);
		if (sub < 0)
			return false;
	}
	int market = m_markets.Add(str + seqend + 1);
	if (market < 0)
		return false;

	key.first.id = id;
	key.first.seq = seq;
	key.first.nzero = nzero;
	key.second.year = year - kYearBase;
	key.second.head = str[0];
	key.second.type = (char)type;
	key.second.sub = (start + kTypeLen + kSubLen == end) ? sub : 0;
	key.second.market = (char)market;
	key.SetValue(0);
	return true;
}

int BondKeyParser::GetIntLength(const char* str, int len) const
{
	int i = 0;
	for (; i < len; i++)
	{
		if (str[i]<'0' || str[i]>'9')
			return i;
	}
	return i >= len ? len : (-1);
}

int BondKeyParser::GetTypeRange(const char* str, int len, int& end)
{
	const int default_start = 11;//大多数债券的类型编码的起始位置,11
	int pos = 1;
	int start = -1;
	end = -1;
	if (default_start < len && IsAlpha(str[default_start]) && !IsAlpha(str[default_start - 1]))
	{
		start = default_start;
		pos = start + 1;
	}
	for (; pos < len; pos++)
	{
		if (start < 0){
			if (IsAlpha(str[pos]))
				start = pos;
		}
		else if (!IsAlpha(str[pos]))
		{
			end = pos;
			break;
		}
	}
	return start;
}

BondCodeParser::BondCodeParser()
{
	const char* marks = ".IB,.SZ,.SH";
	m_markets.Init(marks);
}

bool BondCodeParser::Parse(const char* str, int len, BondCodeCompact& code)
{
	if (!str || len <= 0)
		return false;
	// 000  数字  一个字母 数字 .字符串
	int num = 0, seq = 0;
	const char* ptr = str; int plen = len;
	int nzero = TestZero(ptr, plen); ptr += nzero; plen -= nzero;
	if (nzero > MaxZero)
		return false;
	int nnum = TestNum(ptr, plen, num); ptr += nnum; plen -= nnum;
	code.id = 0; code.index = 0;
	if (ptr[0] >= 'A' && ptr[0] <= 'Z')
	{
		++ptr; ++plen;
		if (ptr[0] == '0')
			return false;
		int nseq = TestNum(ptr, plen, seq); ptr += nseq; plen -= nseq;
		if (seq > MaxSeq || num > MaxShortId)
			return false;
		code.first.id = num;
		code.first.code = 1 + ptr[0] - 'A';
		code.first.seq = seq;
		code.second.flag = 1;
	}
	else
	{
		if (nnum >= 12)//针对12个数字的做特殊检查
			return false;
		code.id = num;
	}
	if (plen != 3 || ptr[0] != '.')
		return false;
	code.second.zero = nzero;
	code.second.market = m_markets.Add(ptr);
	return true;
}
